package main

import (
	"bufio"
	"flag"
	"fmt"
	"io"
	"os"
	"os/exec"
	"strconv"
	"strings"
	"sync"
	"time"
)
/*
采用递归函数，生成不定长度的字典
*/
func face(temp string,index int,l int){
	if stat == 1{
		return
	}
	if len(temp) == l{
		pipe <- temp
		return
	}
	for i:=index;i<len(sum);i++{
		face(temp+sum[i],index,l)
	}
}

/*
消费者函数
*/
func consumer(pipe chan string){
	//无限阻塞，直到爆破到密码或生产者函数主动关闭信道
	for s := range pipe{
		cmd := exec.Command("cmd", "/C",fmt.Sprintf("unrarchs t %s -p%s",*file,s))
		data , _ := cmd.CombinedOutput()
		f := strings.Contains(string(data),"CRC")
		g := strings.Contains(string(data),"???")
		counter += 1
		fmt.Printf("payload:%d\r",counter)
		if !f && !g{
			stat = 1
			fmt.Println("password is "+s)
			if *d == ""{
				wg.Done()
			}
			break
		}
	}
	wg.Done()
}
func producer(pipe chan string,switch_btn int,d string,c string,l int) {
	if switch_btn == 0{
		//生成大写的26个字母
		var word_upper []string
		for i := 65; i <= 90; i++ {
			word_upper = append(word_upper, string(i))
		}
		//生成小写的26个字母
		var word_lowwer []string
		for i := 97; i <= 122; i++ {
			word_lowwer = append(word_lowwer, string(i))
		}
		//0-9数字
		var num []string
		for i:=0;i<10;i++{
			a := strconv.Itoa(i)
			num = append(num, a)
		}
		for _ , i := range c{
			if string(i) == "a" {
				sum = append(append(sum,word_lowwer...))
			}
			if string(i) == "A" {
				sum = append(append(sum,word_upper...))
			}
			if string(i) == "1" {
				sum = append(append(sum,num...))
			}
		}
		face("",0,l)
	}else {
		if switch_btn == 1{
			file_ptr , err := os.Open(d)
			if err != nil{
				fmt.Println(err.Error())
			}
			reader := bufio.NewReader(file_ptr)
			for  {
				s , err := reader.ReadString('\n')
				if err != nil{
					if err == io.EOF {
						break
					}
				}
				pipe <- s
			}
		}else {
			fmt.Println("未知错误")
		}
	}
	time.Sleep(time.Second * 1)
	if stat != 1 {
		fmt.Println("爆破失败...")
	}
	close(pipe)
	wg.Done()
}

//等待协程
var wg sync.WaitGroup
//一个双向信道，用于接受发送数据。
var pipe chan string
//sum用于存放自动生成的字典
var sum []string
//stat用于判断是否成功爆破出密码，爆破成功后生产者协程退出,关闭信道
var stat int
//file为rar压缩包文件名
var file *string
//d为密码字典文件名
var d *string
var counter int
func main()  {
	/*
	定义参数变量
	*/
	file = flag.String("file","","rar压缩包文件")
	c := flag.String("c","","爆破方式,与fcrackzip的使用方式相同,aA1")
	l := flag.Int("l",0,"密码长度")
	d = flag.String("d","","密码字典")
	//参数解析
	flag.Parse()
	//初始化一些变量
	stat = 0
	counter = 0
	wg.Add(2)
	//检查输入参数
	if *file == ""{
		fmt.Println("输入指定的rar压缩文件名")
	}
	switch_btn := -1
	if *c != "" && *l != 0 && *d == ""{
		switch_btn += 1
	}else {
		if *c == "" && *l == 0 && *d != "" {
			switch_btn += 2
		}else {
			fmt.Printf("Error:选项冲突,请选择字典爆破或者内置字典爆破其中的一种\n")
			return
		}
	}
	//缓冲区长度为1024
	pipe = make(chan string,1024)
	//开启生产者消费者模型进行爆破
	go producer(pipe,switch_btn,*d,*c,*l)
	go consumer(pipe)
	//等待生产者消费者模型完成任务
	fmt.Println("fcrackrar by Corn && Bug请联系qq:2635918064\n")
	wg.Wait()
}
